Pelajari cara menangani dan menyebarkan error di React dengan custom hook dan error boundary, memastikan pengalaman pengguna yang tangguh saat terjadi kegagalan muat sumber daya.
Propagasi Error pada Hook 'use' React: Menguasai Rantai Error Pemuatan Sumber Daya
Aplikasi React modern sering kali mengandalkan pengambilan data dari berbagai sumber – API, basis data, atau bahkan penyimpanan lokal. Ketika operasi pemuatan sumber daya ini gagal, sangat penting untuk menangani error dengan baik dan memberikan pengalaman yang berarti bagi pengguna. Artikel ini membahas cara mengelola dan menyebarkan error secara efektif dalam aplikasi React menggunakan custom hook, error boundary, dan strategi penanganan error yang tangguh.
Memahami Tantangan Propagasi Error
Dalam pohon komponen React yang tipikal, error dapat terjadi di berbagai level. Sebuah komponen yang mengambil data mungkin mengalami error jaringan, error parsing, atau error validasi. Idealnya, error ini harus ditangkap dan ditangani dengan tepat, tetapi sekadar mencatat error di komponen tempat asalnya sering kali tidak cukup. Kita memerlukan mekanisme untuk:
- Melaporkan error ke lokasi terpusat: Ini memungkinkan untuk pencatatan (logging), analitik, dan potensi percobaan ulang.
- Menampilkan pesan error yang ramah pengguna: Alih-alih UI yang rusak, informasikan pengguna tentang masalah tersebut dan sarankan solusi yang memungkinkan.
- Mencegah kegagalan beruntun: Error di satu komponen tidak boleh merusak seluruh aplikasi.
Di sinilah propagasi error berperan. Propagasi error melibatkan penerusan error ke atas pohon komponen hingga mencapai batas penanganan error yang sesuai. Error boundary React dirancang untuk menangkap error yang terjadi selama rendering, metode siklus hidup, dan konstruktor dari komponen turunannya, tetapi mereka tidak secara inheren menangani error yang dilemparkan dalam operasi asinkron seperti yang dipicu oleh useEffect. Di sinilah custom hook dapat menjembatani kesenjangan tersebut.
Memanfaatkan Custom Hook untuk Penanganan Error
Custom hook memungkinkan kita untuk merangkum logika yang dapat digunakan kembali, termasuk penanganan error, dalam satu unit yang dapat disusun. Mari kita buat custom hook, useFetch, yang menangani pengambilan data dan manajemen error.
Contoh: Hook useFetch Dasar
Berikut adalah versi sederhana dari hook useFetch:
import { useState, useEffect } from 'react';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const json = await response.json();
setData(json);
setError(null); // Hapus error sebelumnya
} catch (e) {
setError(e);
} finally {
setLoading(false);
}
};
fetchData();
}, [url]);
return { data, loading, error };
}
export default useFetch;
Hook ini mengambil data dari URL yang diberikan dan mengelola status pemuatan serta potensi error. Variabel state error menampung setiap error yang terjadi selama proses pengambilan.
Menyebarkan Error ke Atas
Sekarang, mari kita tingkatkan hook ini untuk menyebarkan error ke atas menggunakan context. Ini memungkinkan komponen induk untuk diberi tahu tentang error yang terjadi di dalam hook useFetch.
1. Buat Error Context
Pertama, kita membuat React context untuk menampung fungsi penangan error:
import { createContext, useContext } from 'react';
const ErrorContext = createContext(null);
export const ErrorProvider = ErrorContext.Provider;
export const useError = () => useContext(ErrorContext);
2. Modifikasi Hook useFetch
Sekarang, kita memodifikasi hook useFetch untuk menggunakan error context:
import { useState, useEffect } from 'react';
import { useError } from './ErrorContext';
function useFetch(url) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [localError, setLocalError] = useState(null); // State error lokal
const handleError = useError(); // Dapatkan penangan error dari context
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const json = await response.json();
setData(json);
setLocalError(null);
} catch (e) {
setLocalError(e);
if (handleError) {
handleError(e); // Sebarkan error ke context
}
} finally {
setLoading(false);
}
};
fetchData();
}, [url, handleError]);
// Kembalikan data dan error lokal. Komponen dapat memutuskan mana yang akan ditampilkan.
return { data, loading, localError };
}
export default useFetch;
Perhatikan bahwa kita sekarang memiliki dua status error: localError, yang dikelola di dalam hook, dan error yang disebarkan melalui context. Kita menggunakan localError secara internal, tetapi juga dapat diakses untuk penanganan di tingkat komponen.
3. Bungkus Aplikasi dengan ErrorProvider
Di root aplikasi Anda, bungkus komponen yang menggunakan useFetch dengan ErrorProvider. Ini menyediakan context penanganan error ke semua komponen turunan:
import React, { useState } from 'react';
import { ErrorProvider } from './ErrorContext';
import MyComponent from './MyComponent';
function App() {
const [globalError, setGlobalError] = useState(null);
const handleError = (error) => {
console.error("Error ditangkap di tingkat atas:", error);
setGlobalError(error);
};
return (
{globalError ? (
Error: {globalError.message}
) : (
)}
);
}
export default App;
4. Menggunakan Hook useFetch di dalam Komponen
import React from 'react';
import useFetch from './useFetch';
function MyComponent() {
const { data, loading, localError } = useFetch('https://api.example.com/data');
if (loading) {
return Memuat...
;
}
if (localError) {
return Error memuat data: {localError.message}
;
}
return (
Data:
{JSON.stringify(data, null, 2)}
);
}
export default MyComponent;
Penjelasan
- Error Context:
ErrorContextmenyediakan cara untuk berbagi fungsi penanganan error (handleError) di seluruh komponen. - Propagasi Error: Ketika error terjadi di
useFetch, fungsihandleErrordipanggil, menyebarkan error ke komponenApp. - Penanganan Error Terpusat: Komponen
Appsekarang dapat menangani error secara terpusat, mencatatnya, menampilkan pesan error, atau mengambil tindakan lain yang sesuai.
Error Boundaries: Jaring Pengaman untuk Error Tak Terduga
Meskipun custom hook dan context menyediakan cara untuk menangani error dari operasi asinkron, Error Boundaries sangat penting untuk menangkap error tak terduga yang mungkin terjadi selama rendering. Error Boundaries adalah komponen React yang menangkap error JavaScript di mana pun dalam pohon komponen turunannya, mencatat error tersebut, dan menampilkan UI fallback alih-alih pohon komponen yang rusak. Mereka menangkap error selama rendering, dalam metode siklus hidup, dan dalam konstruktor dari seluruh pohon di bawahnya.
Membuat Komponen Error Boundary
import React from 'react';
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null, errorInfo: null };
}
static getDerivedStateFromError(error) {
// Perbarui state agar render berikutnya menampilkan UI fallback.
return { hasError: true, error: error };
}
componentDidCatch(error, errorInfo) {
// Anda juga dapat mencatat error ke layanan pelaporan error
console.error("Error ditangkap di ErrorBoundary:", error, errorInfo);
this.setState({errorInfo: errorInfo});
}
render() {
if (this.state.hasError) {
// Anda dapat me-render UI fallback kustom apa pun
return (
Terjadi kesalahan.
{this.state.error && this.state.error.toString()}\n
{this.state.errorInfo && this.state.errorInfo.componentStack}
);
}
return this.props.children;
}
}
export default ErrorBoundary;
Menggunakan Error Boundary
Bungkus komponen apa pun yang berpotensi melemparkan error dengan komponen ErrorBoundary:
import React from 'react';
import ErrorBoundary from './ErrorBoundary';
import MyComponent from './MyComponent';
function App() {
return (
);
}
export default App;
Menggabungkan Error Boundaries dan Custom Hook
Untuk penanganan error yang paling tangguh, gabungkan Error Boundaries dengan custom hook seperti useFetch. Error Boundaries menangkap error rendering yang tidak terduga, sementara custom hook mengelola error dari operasi asinkron dan menyebarkannya ke atas. ErrorProvider dan ErrorBoundary dapat hidup berdampingan; ErrorProvider memungkinkan penanganan dan pelaporan error yang granular, sementara ErrorBoundary mencegah kerusakan aplikasi yang fatal.
Praktik Terbaik untuk Penanganan Error di React
- Pencatatan Error Terpusat: Kirim error ke layanan pencatatan terpusat untuk pemantauan dan analisis. Layanan seperti Sentry, Rollbar, dan Bugsnag adalah pilihan yang bagus. Pertimbangkan untuk menggunakan tingkat pencatatan (misalnya, `console.error`, `console.warn`, `console.info`) untuk membedakan tingkat keparahan peristiwa.
- Pesan Error yang Ramah Pengguna: Tampilkan pesan error yang jelas dan membantu kepada pengguna. Hindari jargon teknis dan berikan saran untuk menyelesaikan masalah. Pikirkan tentang lokalisasi: pastikan pesan error dapat dimengerti oleh pengguna dalam berbagai bahasa dan konteks budaya.
- Degradasi Anggun (Graceful Degradation): Rancang aplikasi Anda untuk menurun secara anggun jika terjadi error. Misalnya, jika panggilan API tertentu gagal, sembunyikan komponen yang sesuai atau tampilkan placeholder alih-alih merusak seluruh aplikasi.
- Mekanisme Coba Lagi (Retry): Terapkan mekanisme coba lagi untuk error sementara, seperti gangguan jaringan. Namun, berhati-hatilah untuk menghindari loop coba lagi yang tak terbatas, yang dapat memperburuk masalah. Peningkatan waktu tunda secara eksponensial (Exponential backoff) adalah strategi yang baik.
- Pengujian: Uji logika penanganan error Anda secara menyeluruh untuk memastikan bahwa itu berfungsi seperti yang diharapkan. Simulasikan skenario error yang berbeda, seperti kegagalan jaringan, data tidak valid, dan error server. Pertimbangkan untuk menggunakan alat seperti Jest dan React Testing Library untuk menulis pengujian unit dan integrasi.
- Pemantauan: Pantau aplikasi Anda secara terus-menerus untuk error dan masalah kinerja. Siapkan peringatan untuk diberitahu ketika error terjadi, memungkinkan Anda untuk merespons masalah dengan cepat.
- Pertimbangkan Keamanan: Cegah informasi sensitif ditampilkan dalam pesan error. Hindari menyertakan jejak tumpukan (stack traces) atau detail server internal dalam pesan yang dihadapi pengguna, karena informasi ini dapat dieksploitasi oleh pihak jahat.
Teknik Penanganan Error Tingkat Lanjut
Menggunakan Solusi Manajemen State Error Global
Untuk aplikasi yang lebih kompleks, pertimbangkan untuk menggunakan solusi manajemen state global seperti Redux, Zustand, atau Recoil untuk mengelola state error. Ini memungkinkan Anda mengakses dan memperbarui state error dari mana saja di aplikasi Anda, menyediakan cara terpusat untuk menangani error. Misalnya, Anda dapat mengirimkan aksi untuk memperbarui state error ketika error terjadi dan kemudian menggunakan selector untuk mengambil state error di komponen mana pun.
Mengimplementasikan Kelas Error Kustom
Buat kelas error kustom untuk mewakili berbagai jenis error yang dapat terjadi di aplikasi Anda. Ini memungkinkan Anda untuk dengan mudah membedakan antara berbagai jenis error dan menanganinya dengan sesuai. Misalnya, Anda bisa membuat kelas NetworkError, kelas ValidationError, dan kelas ServerError. Ini akan membuat logika penanganan error Anda lebih terorganisir dan mudah dipelihara.
Menggunakan Pola Circuit Breaker
Pola circuit breaker adalah pola desain yang dapat membantu mencegah kegagalan beruntun dalam sistem terdistribusi. Ide dasarnya adalah membungkus panggilan ke layanan eksternal dalam objek circuit breaker. Jika circuit breaker mendeteksi sejumlah kegagalan tertentu, ia akan "membuka" sirkuit dan mencegah panggilan lebih lanjut ke layanan eksternal. Setelah jangka waktu tertentu, circuit breaker akan "setengah membuka" sirkuit dan mengizinkan satu panggilan ke layanan eksternal. Jika panggilan berhasil, circuit breaker akan "menutup" sirkuit dan memungkinkan semua panggilan ke layanan eksternal untuk dilanjutkan. Ini dapat membantu mencegah aplikasi Anda kewalahan oleh kegagalan di layanan eksternal.
Pertimbangan Internasionalisasi (i18n)
Ketika berhadapan dengan audiens global, internasionalisasi adalah yang terpenting. Pesan error harus diterjemahkan ke dalam bahasa pilihan pengguna. Pertimbangkan untuk menggunakan pustaka seperti i18next untuk mengelola terjemahan secara efektif. Selain itu, waspadai perbedaan budaya dalam cara error dipersepsikan. Misalnya, pesan peringatan sederhana mungkin diinterpretasikan secara berbeda di berbagai budaya, jadi pastikan nada dan kata-katanya sesuai untuk audiens target Anda.
Skenario Error Umum dan Solusinya
Error Jaringan
Skenario: Server API tidak tersedia, atau koneksi internet pengguna terputus.
Solusi: Tampilkan pesan yang menunjukkan ada masalah jaringan dan sarankan untuk memeriksa koneksi internet. Terapkan mekanisme coba lagi dengan peningkatan waktu tunda secara eksponensial (exponential backoff).
Data Tidak Valid
Skenario: API mengembalikan data yang tidak sesuai dengan skema yang diharapkan.
Solusi: Terapkan validasi data di sisi klien untuk menangkap data yang tidak valid. Tampilkan pesan error yang menunjukkan bahwa data rusak atau tidak valid. Pertimbangkan untuk menggunakan TypeScript untuk memberlakukan tipe data pada waktu kompilasi.
Error Autentikasi
Skenario: Token autentikasi pengguna tidak valid atau kedaluwarsa.
Solusi: Arahkan pengguna ke halaman login. Tampilkan pesan yang menunjukkan bahwa sesi mereka telah berakhir dan mereka perlu login lagi.
Error Otorisasi
Skenario: Pengguna tidak memiliki izin untuk mengakses sumber daya tertentu.
Solusi: Tampilkan pesan yang menunjukkan bahwa mereka tidak memiliki izin yang diperlukan. Sediakan tautan untuk menghubungi dukungan jika mereka yakin seharusnya memiliki akses.
Error Server
Skenario: Server API mengalami error yang tidak terduga.
Solusi: Tampilkan pesan error generik yang menunjukkan ada masalah dengan server. Catat error di sisi server untuk tujuan debugging. Pertimbangkan untuk menggunakan layanan seperti Sentry atau Rollbar untuk melacak error server.
Kesimpulan
Penanganan error yang efektif sangat penting untuk menciptakan aplikasi React yang tangguh dan ramah pengguna. Dengan menggabungkan custom hook, error boundary, dan strategi penanganan error yang komprehensif, Anda dapat memastikan bahwa aplikasi Anda menangani error dengan baik dan memberikan pengalaman yang berarti bagi pengguna, bahkan selama kegagalan pemuatan sumber daya. Ingatlah untuk memprioritaskan pencatatan error terpusat, pesan error yang ramah pengguna, dan degradasi yang anggun. Dengan mengikuti praktik terbaik ini, Anda dapat membangun aplikasi React yang tangguh, andal, dan mudah dipelihara, terlepas dari lokasi atau latar belakang pengguna Anda.